<template>
  <div class="relative" @mouseenter="onMouseenter" @mouseleave="onMouseleave">
    <div ref="referenceTarget">
      <!-- 具名插槽 触发弹层的试图 -->
      <slot name="reference" />
    </div>
    <!-- 气泡展示 -->
    <transition name="slide">
      <div
        ref="contentTarget"
        v-if="isVisable"
        :style="contentStyle"
         class="absolute p-1 z-20 bg-white dark:bg-zinc-900 border rounded-md dark:border-zinc-700"
      >
        <!-- 匿名插槽 弹出层中展示的内容  -->
        <slot />
      </div>
    </transition>
  </div>
</template>
<script>
// 1、指定所有可选位置的常量，生成一个list
// 左上
const PROP_LEFT_TOP = 'left-top'
// 左下
const PROP_LEFT_BOTTOM = 'left-bottom'
// 右上
const PROP_RIGHT_TOP = 'right-top'
// 右下
const PROP_RIGHT_BOTTOM = 'right-bottom'

const palcementList = [
  PROP_LEFT_TOP,
  PROP_LEFT_BOTTOM,
  PROP_RIGHT_TOP,
  PROP_RIGHT_BOTTOM
]
</script>

<script setup>
import { ref, reactive, toRefs, onMounted, watch, nextTick } from 'vue'

const props = defineProps({
  //   2、通过props  参数，指定可选位置
  placement: {
    type: String,
    default: () => PROP_LEFT_BOTTOM,
    validator(val) {
      // 校验placement 是否在list中
      return palcementList.includes(val)
        ? true
        : new Error(`placement必须是${palcementList}中的一个`)
    }
  },
  // 触发方式
  trigger: {
    type: String,
    default: 'hover'
  }
})
const isVisable = ref(false)

// 延迟关闭时间
const delayTime = 150
let timeOut = null
const onMouseenter = () => {
  isVisable.value = true
  if (timeOut) {
    clearTimeout(timeOut)
  }
}
const onMouseleave = () => {
  timeOut = setTimeout(() => {
    isVisable.value = false
    timeOut = null
  }, delayTime)
}

/*
  1、指定所有可选位置的常量，生成一个list
  2、通过props  参数，指定可选位置
  3、获取元素的DOM创建读取元素的尺寸的方法
  4、生成气泡的样式对象，用来控制每个位置对应的样式
  5、根据props计算样式对象
*/

// 3、获取元素的DOM创建读取元素的尺寸的方法
const referenceTarget = ref(null)
const contentTarget = ref(null)
const getElementSize = (target) => {
  if (!target) return {}
  return {
    width: target.offsetWidth,
    height: target.offsetHeight
  }
}
//  4、生成气泡的样式对象，用来控制每个位置对应的样式
const contentStyle = ref({
  top: 0,
  left: 0
})
//  5、根据props计算样式对象
watch(isVisable, (val) => {
  if (!val) return
  nextTick(() => {
    switch (props.placement) {
      // 左上
      case PROP_LEFT_TOP:
        contentStyle.value.top = 0
        contentStyle.value.left = `-${
          getElementSize(contentTarget.value).width
        }px`
        break
      // 左下
      case PROP_LEFT_BOTTOM:
        contentStyle.value.top = `${
          getElementSize(referenceTarget.value).height
        }px`
        contentStyle.value.left = `-${
          getElementSize(contentTarget.value).width
        }px`
        break
      // 右上
      case PROP_RIGHT_TOP:
        contentStyle.value.top = 0
        contentStyle.value.left = `${
          getElementSize(referenceTarget.value).width
        }px`
        break
      // 右下
      case PROP_RIGHT_BOTTOM:
        contentStyle.value.top = `${
          getElementSize(referenceTarget.value).height
        }px`
        contentStyle.value.left = `${
          getElementSize(referenceTarget.value).width
        }px`
        break
      default:
        break
    }
  })
})
</script>
<style scoped lang="scss">
.slide-enter-active,
.slide-leave-active {
  transition: opacity 0.3s, transform 0.3s;
}
.slide-enter-from,
.slide-leave-to {
  opacity: 0;
  transform: translateY(20px);
}
</style>
